#ifndef NANE_ENUM_H
#   define NANE_ENUM_H

#   include "types.h"

namespace nane 
{
	template <typename E, typename BaseType = uint32>
	struct Enum
	{
            typedef BaseType raw_type;

            //Enum() {}
            Enum( const raw_type _raw_value ) 
                : m_rawValue( _raw_value ) 
            {
            }

            raw_type raw_value() const
            {
                return m_rawValue;
            }

            bool operator == ( const Enum& _rhs ) const
            {
                return m_rawValue == _rhs.m_rawValue;
            }
            
            bool operator < ( const Enum& _rhs ) const
            {
                return m_rawValue < _rhs.m_rawValue;
            }

            bool operator <= ( const Enum& _rhs ) const
            {
                return m_rawValue <= _rhs.m_rawValue;
            }
            
            bool operator !=( const Enum& _rhs ) const
            {
                return m_rawValue != _rhs.m_rawValue;
            }
            
            bool operator > ( const Enum& _rhs ) const
            {
                return m_rawValue > _rhs.m_rawValue;
            }

            bool operator >= ( const Enum& _rhs ) const
            {
                return m_rawValue >= _rhs.m_rawValue;
            }
            
        protected:
            raw_type m_rawValue;
	};
    
#   define NANE_ENUM_TYPE( EnumType, EnumBase, BaseType )\
    friend struct EnumBase<EnumType, BaseType>;\
    static const uint32 size = _max;\
    EnumType( domain _value )\
        : EnumBase<EnumType, BaseType>( _value )\
    {\
    }
    
#   define NANE_ENUM( EnumType, EnumBase )\
    friend struct EnumBase<EnumType>;\
    static const uint32 size = _max;\
    EnumType( domain _value )\
        : EnumBase<EnumType>( _value )\
    {\
    } 
    
	template <typename E, typename BaseType = uint32>
	struct EnumBitwise
        : public Enum<E, BaseType>
	{
        EnumBitwise( const typename Enum<E, BaseType>::raw_type _raw_value ) 
            : Enum<E, BaseType>( _raw_value ) 
        {
        }
        
        E operator&( E _rhs )
        {
            return E(Enum<E, BaseType>::m_rawValue & _rhs.raw_value());
        }

        E operator|( E _rhs )
        {
            return E(Enum<E, BaseType>::m_rawValue | _rhs.raw_value());
        }

        E operator^( E _rhs )
        {
            return E(Enum<E, BaseType>::m_rawValue ^ _rhs.raw_value());
        }

        E& operator|=( E _rhs )
        {
            Enum<E, BaseType>::m_rawValue |= _rhs.raw_value();
            return *static_cast<E*>(this);
        }

        E& operator&=( E _rhs )
        {
            Enum<E, BaseType>::m_rawValue &= _rhs.raw_value();
            return *static_cast<E*>(this);
        }

        E& operator^=( E _rhs )
        {
            Enum<E, BaseType>::m_rawValue ^= _rhs.raw_value();
            return *static_cast<E*>(this);
        }

        E operator~()
        {
            return E(~Enum<E, BaseType>::m_rawValue);
        }
        
	};  
    
#   define NANE_ENUM_BITWISE( EnumType )\
    inline EnumType operator | ( EnumType::domain _lhs, EnumType::domain _rhs )\
    {\
        return EnumType(static_cast<EnumType::domain>(static_cast<EnumType::raw_type>(_lhs) | static_cast<EnumType::raw_type>(_rhs)));\
    }\
    inline EnumType operator&( EnumType::domain _lhs, EnumType::domain _rhs )\
    {\
        return EnumType(static_cast<EnumType::domain>(static_cast<EnumType::raw_type>(_lhs) & static_cast<EnumType::raw_type>(_rhs)));\
    }\
    inline EnumType operator^( EnumType::domain _lhs, EnumType::domain _rhs )\
    {\
        return EnumType(static_cast<EnumType::domain>(static_cast<EnumType::raw_type>(_lhs) ^ static_cast<EnumType::raw_type>(_rhs)));\
    }\
    inline EnumType operator~( EnumType::domain _rhs )\
    {\
        return EnumType(static_cast<EnumType::domain>(~static_cast<EnumType::raw_type>(_rhs)));\
    }

    template<typename E>
    inline E enum_cast( const typename E::raw_type _raw_value )
    {
        return E(static_cast<typename E::domain>(_raw_value));
    }
}   // namespace nane

#endif	// NANE_ENUM_H
